/** @file    SystemLogger.cpp
 *  @author  Alessandro Polo
 *  @version $Id: SystemLogger.cpp 2832 2010-08-02 22:53:46Z alex $
 *  @brief
 * File containing methods for the wosh::SystemLogger class.
 * The header for this class can be found in SystemLogger.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/
 
 #include <core/SystemLogger.h>
 #include <core/ObjectAllocator.h>
 #include <core/ObjectFactory.h>
 #include <core/WoshKernel.h>
 #include <core/SystemError.h>
 #include <core/MethodsCommon.h>
 #include <core/Table.h>
 #include <core/List.h>
 #include <core/Functional.h>
 #include <core/DataModels.h>
 #include <core/Utilities.h>
 #include <core/LogContext.h>
 #include <core/LogHandlers.h>


using namespace std;
namespace wosh {

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Gamma singleton! static reference is allocated and maintained by WoshKernel
extern "C" {
	SystemLogger* SystemLogger::getInstance() { 
		if ( WoshKernel::getInstance() == NULL ) return NULL;
		return &WoshKernel::getInstance()->logger();
	}
};

 // register as abstract because it is a singleton:
 WOSH_REGISTER_INTERFACE(wosh::SystemLogger, "wosh::WoshModule", 0.01, _static_SystemLogger )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

SystemLogger::SystemLogger( const WoshKernel* parent )
	: WoshModule(parent, _SystemLogger_NAME, _SystemLogger_URI) {

//	Properties.set( _SystemLogger_KEY_Count, 0, Permission( Permission::Read ) );

	Log(LOG_DEBUG, " Registering Methods.." );
	MethodList* mmListContexts = Methods.create<MethodList>( _METHOD_List, "List Contexts", Permission::R_XR_XR__() );
	mmListContexts->setDataFieldExtractor( DataFieldExtractor<LogContext>::createFor< DataAdapterListT<LogContext> >(LogContext::getInstances(), "Log Contexts") );

	MethodList* mmListHandlers = Methods.create<MethodList>( _SystemLogger_METHOD_list_handlers, "List Handlers", Permission::R_XR_XR__() );
	mmListHandlers->setDataFieldExtractor( DataFieldExtractor<LogHandler>::createFor< DataAdapterListT<LogHandler> >(LogHandler::getInstances(), "Log Handlers") );

	MethodRequest* mmAddHandler = Methods.create<MethodRequest>( _SystemLogger_METHOD_add_handler, "Add Handler", Permission::R_XR__R__() );
	mmAddHandler->setMethod( this, (MethodRequestPtr)&SystemLogger::mmDoAddHandler );
	MethodRequest* mmRemoveHandler = Methods.create<MethodRequest>( _SystemLogger_METHOD_remove_handler, "Remove Handler", Permission::R_XR__R__() );
	mmRemoveHandler->setMethod( this, (MethodRequestPtr)&SystemLogger::mmDoRemoveHandler );
	MethodRequest* mmLinkHandler = Methods.create<MethodRequest>( _SystemLogger_METHOD_link_handler, "Link Context to Handler", Permission::R_XR__R__() );
	mmLinkHandler->setMethod( this, (MethodRequestPtr)&SystemLogger::mmDoLinkHandler );
	MethodRequest* mmSwapHandler = Methods.create<MethodRequest>( _SystemLogger_METHOD_swap_handler, "Swap Handler in all Contexts", Permission::R_XR__R__() );
	mmSwapHandler->setMethod( this, (MethodRequestPtr)&SystemLogger::mmDoSwapHandler );
	MethodRequest* mmEditHandler = Methods.create<MethodRequest>( _SystemLogger_METHOD_edit_handler, "Change Handler's protocol", Permission::R_XR__R__() );
	mmEditHandler->setMethod( this, (MethodRequestPtr)&SystemLogger::mmDoEditHandler );

 }

SystemLogger::~SystemLogger() {
	Log(LOG_DEBUG, ":~SystemLogger() Destroying.." );

	Log(LOG_VERBOSE, " Destroyed." );
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////// KERNEL EVENTS

WRESULT SystemLogger::onKernelInitializing() {
	WRESULT ret = WoshModule::onKernelInitializing();
	return ret;
 }

/////////////////////////////////////////////////////////////////////////////////////////////// KERNEL EVENTS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT SystemLogger::installHandler( const std::string& handler_type, const std::string& handler_protocol ) {
	Log(LOG_INFO, ":installHandler(%s,%s)", handler_type.c_str(), handler_protocol.c_str() );
	LogHandler* handler = ObjectFactory::createTypeOf<LogHandler>(handler_type, 0.0);
	if ( handler == NULL )
		return WRET_ERR_PARAM;
	long handler_id = handler->getID(); /// WARNING: unsafe access, but we feel lucky :)
	if ( !handler_protocol.empty() )
		return WRET_OK;
	return editHandler( handler_id, handler_protocol );
 }

WRESULT SystemLogger::removeHandler( long handler_id ) {
	Log(LOG_INFO, ":mmDoRemoveHandler(%ld)", handler_id );
	LogHandler::getInstances().transactionBeginWrite();
	ListT<LogHandler*>::ConstIterator it_f = std::find_if(LogHandler::getInstances().begin(), LogHandler::getInstances().end(),
		Functor_Validator<LogHandler, long>(handler_id, &LogHandler::getID ) );
	if ( it_f == LogHandler::getInstances().end() || *it_f == NULL ) {
		LogHandler::getInstances().transactionEnd();
		return WRET_ERR_PARAM;
	 }
	// recursive!? what if it IN multiplexes?

	// unlink all contexts
	swapHandler_(*it_f, NULL);

	WRESULT closed = (*it_f)->close();
	if ( WFAILED(closed) ) {
		LogHandler::getInstances().transactionEnd();
		return WRET_ERR_INTERNAL;
	 }

	(*it_f)->skip_unregister_instance = true;
	delete *it_f;
	LogHandler::getInstances().transactionEnd();
	return WRET_OK;
 }

int SystemLogger::swapHandler_( LogHandler* handler, LogHandler* new_handler ) {
	int count = 0;
	LogContext::getInstances().transactionBeginRead();
	ListT<LogContext*>::ConstIterator it;
	const ListT<LogContext*>::ConstIterator it_end = LogContext::getInstances().end();
	for ( it=LogContext::getInstances().begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		(*it)->configMux.lockForWrite();
		if ( (*it)->handler == handler ) { // replace old handler (overwrite pointer)
			(*it)->handler = new_handler;
			++count;
		 }
		(*it)->configMux.unLock();
	 }
	LogContext::getInstances().transactionEnd();
	return count;
 }

WRESULT SystemLogger::editHandler( long handler_id, const std::string& handler_protocol ) {
	LogHandler::getInstances().transactionBeginRead();
	ListT<LogHandler*>::ConstIterator it_f = std::find_if(LogHandler::getInstances().begin(), LogHandler::getInstances().end(),
		Functor_Validator<LogHandler, long>(handler_id, &LogHandler::getID ) );
	if ( it_f == LogHandler::getInstances().end() || *it_f == NULL ) {
		LogHandler::getInstances().transactionEnd();
		return WRET_ERR_PARAM;
	 }
	(*it_f)->setProtocol(handler_protocol);
	LogHandler::getInstances().transactionEnd();
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT SystemLogger::linkHandler( long handler_id, const std::string& context_name ) {
	LogHandler::getInstances().transactionBeginRead();
	ListT<LogHandler*>::ConstIterator it_f = std::find_if(LogHandler::getInstances().begin(), LogHandler::getInstances().end(),
		Functor_Validator<LogHandler, long>(handler_id, &LogHandler::getID ) );
	if ( it_f == LogHandler::getInstances().end() || *it_f == NULL ) {
		LogHandler::getInstances().transactionEnd();
		return WRET_ERR_PARAM;
	 }
	LogContext::getInstances().transactionBeginRead();
	ListT<LogContext*>::ConstIterator it_c = std::find_if(LogContext::getInstances().begin(), LogContext::getInstances().end(),
		Functor_Validator<LogContext, const std::string&>(context_name, &LogContext::getContext ) );
	if ( it_c == LogContext::getInstances().end() || *it_c == NULL ) {
		LogContext::getInstances().transactionEnd();
		LogHandler::getInstances().transactionEnd();
		return WRET_ERR_PARAM;
	 }
	(*it_c)->configMux.lockForWrite();
	(*it_c)->handler = *it_f;
	(*it_c)->configMux.unLock();
	LogContext::getInstances().transactionEnd();
	LogHandler::getInstances().transactionEnd();
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////// METHODS

Response* SystemLogger::mmDoAddHandler( const Request* request ) {
	if ( request == NULL ) return NULL;
	Variant dHandlerType; Variant dHandlerProtocol;
	request->extractArguments(dHandlerType, dHandlerProtocol);
	if ( !dHandlerType.isStringNotEmpty() )
		return request->replyResponse(WRET_ERR_PARAM, "invalid bundle type");
	Log(LOG_VERBOSE, ":mmDoAddHandler(%s, %s)", dHandlerType.asString().c_str(), dHandlerProtocol.toString().c_str() );
	WRESULT ret = installHandler( dHandlerType.asString(), dHandlerProtocol.toString() );
	return request->replyResponse(ret);
 }

Response* SystemLogger::mmDoRemoveHandler( const Request* request ) {
	if ( request == NULL ) return NULL;
	long handler_id = request->getArgument().toLong(0);
	Log(LOG_VERBOSE, ":mmDoRemoveHandler(%ld)", handler_id );
	WRESULT ret = removeHandler( handler_id );
	return request->replyResponse(ret);
 }

Response* SystemLogger::mmDoLinkHandler( const Request* request ) {
	if ( request == NULL ) return NULL;
	Variant dHandlerID;
	Variant dcontextName;
	request->extractArguments(dHandlerID, dcontextName);
	if ( !dHandlerID.isNumeric() || !dcontextName.isStringNotEmpty() )
		return request->replyResponse(WRET_ERR_PARAM, "invalid arguments [long,string]");
	WRESULT ret = linkHandler( dHandlerID.toLong(0), dcontextName.asString() );
	return request->replyResponse(ret);
 }

Response* SystemLogger::mmDoSwapHandler( const Request* request ) {
	if ( request == NULL ) return NULL;
	Variant dHandlerID;
	Variant dHandlerReplace;
	request->extractArguments(dHandlerID, dHandlerReplace);
	if ( !dHandlerID.isNumeric() )
		return request->replyResponse(WRET_ERR_PARAM, "invalid handler id");
	if ( !dHandlerReplace.isNumeric() && !dHandlerReplace.isString() )
		return request->replyResponse(WRET_ERR_PARAM, "invalid query [string|long]");
	long handler_id = dHandlerID.toLong(0);
	LogHandler::getInstances().transactionBeginWrite();
	ListT<LogHandler*>::ConstIterator it_f = std::find_if(LogHandler::getInstances().begin(), LogHandler::getInstances().end(),
		Functor_Validator<LogHandler, long>(handler_id, &LogHandler::getID ) );
	LogHandler* handler_key = NULL;
	if ( it_f != LogHandler::getInstances().end() )
		handler_key = *it_f;
	if ( dHandlerReplace.isNumeric() && dHandlerReplace.toLong(0) == 0 ) {
		if ( handler_key == NULL )
			return request->replyResponse(WRET_ERR_PARAM, "Key and Pattern Handler are the same!");
		int count = swapHandler_( handler_key, NULL );
		LogHandler::getInstances().transactionEnd();
		return request->replyResponse(WRET_OK, new Variant(count) );
	 }
	else if ( dHandlerReplace.isNumeric() ) {
		long handler_replace_id = dHandlerReplace.toLong(0);
		ListT<LogHandler*>::ConstIterator it_re = std::find_if(LogHandler::getInstances().begin(), LogHandler::getInstances().end(),
			Functor_Validator<LogHandler, long>(handler_replace_id, &LogHandler::getID ) );
		if ( it_re == LogHandler::getInstances().end() || *it_re == NULL ) {
			LogHandler::getInstances().transactionEnd();
			return request->replyResponse(WRET_ERR_PARAM, "Pattern Handler not found!");
		 }
		int count = swapHandler_( handler_key, *it_re );
		LogHandler::getInstances().transactionEnd();
		return request->replyResponse(WRET_OK, new Variant(count) );
	 }
	else if ( dHandlerReplace.isStringNotEmpty() ) {
		int count = 0;
		LogContext::getInstances().transactionBeginRead();
		ListT<LogContext*>::ConstIterator it;
		const ListT<LogContext*>::ConstIterator it_end = LogContext::getInstances().end();
		for ( it=LogContext::getInstances().begin(); it!=it_end; ++it ) {
			if ( *it == NULL ) continue;
			if ( (*it)->getContext() != dHandlerReplace.asString() ) continue;
			(*it)->configMux.lockForWrite();
			(*it)->handler = handler_key;
			++count;
			(*it)->configMux.unLock();
		 }
		LogContext::getInstances().transactionEnd();
		return request->replyResponse(WRET_OK, new Variant(count) );
	 }
	LogHandler::getInstances().transactionEnd();
	return request->replyResponse(WRET_ERR_INTERNAL, "unrecognized state" );
 }

Response* SystemLogger::mmDoEditHandler( const Request* request ) {
	if ( request == NULL ) return NULL;
	Variant dHandlerID;
	Variant dHandlerSettings;
	request->extractArguments(dHandlerID, dHandlerSettings);
	if ( !dHandlerID.isNumeric() )
		return request->replyResponse(WRET_ERR_PARAM, "invalid handler id");
	long handler_id = dHandlerID.toLong(0);
	Log(LOG_VERBOSE, ":mmDoEditHandler(%ld, %s)", handler_id, dHandlerSettings.toString().c_str() );
	WRESULT ret = editHandler(handler_id, dHandlerSettings.toString() );
	return request->replyResponse(ret);
 }

///////////////////////////////////////////////////////////////////////////////////////////////////// METHODS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

}; // namespace wosh
